#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "tyco_coroutine.h"





#ifdef _USE_UCONTEXT

static void _save_stack(tyco_coroutine *co) {
	char *top = co->sched->stack + co->sched->stack_size;
	char dummy = 0;			// 用作占位符的变量
	assert(top - &dummy <=  TYCO_CO_STACKSIZE_MAX);
	if (co->sched->stack_size < top - &dummy) {
		co->stack = realloc(co->stack, top - &dummy);
		assert(co->stack != NULL);
	}
	co->stack_size = top - &dummy;
	memcpy(co->stack, &dummy, co->stack_size);	// 栈地址是从高到低生长
}


/*
	@brief 将协程的栈内容拷贝调度器的栈空间
*/
static void _load_stack(tyco_coroutine *co) {
	memcpy(co->sched->stack + co->sched->stack_size - co->stack_size, co->stack, co->stack_size);
}


static void _exec(void *arg) {
	tyco_coroutine *co = (tyco_coroutine *)arg;
	co->func(co->arg);
	co->status |= BIT(TYCO_COROUTINE_STATUS_EXITED) | BIT(TYCO_COROUTINE_STATUS_DETACH) | BIT(TYCO_COROUTINE_STATUS_FDEOF);
	tyco_coroutine_yield(co);
}

#else

#endif // _UES_UCONTEXT




static int tyco_coroutine_init(tyco_coroutine *co) {
	if (co == NULL) return -1;

	tyco_schedule *sched = tyco_get_sched();
#ifdef _USE_UCONTEXT
	getcontext(&co->ctx);
	co->ctx.uc_stack.ss_sp = sched->stack;
	co->ctx.uc_stack.ss_size = sched->stack_size;
	co->ctx.uc_link = &sched->ctx;
	makecontext(&co->ctx, _exec, 1, co);
#else
#endif // _USE_UCONTEXT
	co->status  = BIT(TYCO_COROUTINE_STATUS_READY);
}

void tyco_coroutine_free(tyco_coroutine *co) {
	if (co == NULL) return;

	if (co->stack) {
		free(co->stack);
		co->stack = NULL;
	}
	free(co);
}


int tyco_coroutine_yield(tyco_coroutine *co) {
	if (co == NULL) return -1;
	co->ops = 0;
#ifdef _USE_UCONTEXT
	if (co->status & BIT(TYCO_COROUTINE_STATUS_EXITED) == 0) {
		_save_stask(co);
	}
	swapcontext(&co->ctx, &co->sched->ctx);
#else
#endif // _USE_UCONTE
}


int tyco_coroutine_resume(tyco_coroutine *co) {
	if (co == NULL) return -1;

	if (co->status & BIT(TYCO_COROUTINE_STATUS_NEW)) {
		tyco_coroutine_init(co);
	}
#ifdef _USE_UCONTEXT
	else {
		_load_stack(co);
	}
#else
#endif // _USE_UCONTEX
	co->sched->curr_thread = co;
#ifdef _USE_UCONTEXT
	swapcontext(&co->sched->ctx, &co->ctx);
#else
#endif // _USE_UCONTEX
	co->sched->curr_thread = NULL;		// 返回

	if (co->status & BIT(TYCO_COROUTINE_STATUS_EXITED)) {
		if (co->status & BIT(TYCO_COROUTINE_STATUS_DETACH)) {
			printf("nty_coroutine_resume --> \n");
			nty_coroutine_free(co);
		}
		return -1;
	}

	return 0;
}




